package com.JL.engine;

import com.JL.engine.components.CollisionComponent;
import com.JL.engine.components.IdComponent;
import com.JL.engine.components.NameComponent;
import com.JL.engine.components.ViewComponent;
import com.JL.engine.model.*;
import com.JL.engine.model.Component;
import com.JL.engine.util.LocalTimeUtil;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.geom.Point2D;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;

public class GameWorld extends JPanel {
    //存储游戏世界中所有实体对象
    private final ConcurrentLinkedQueue<Entity> ENTITIES = new ConcurrentLinkedQueue<>();
    private  LocalTimeUtil localTimeUtil = new LocalTimeUtil();
    private static final Map<String, EntityFactory> entityFactories = new HashMap<>();
    private static final Map<Enum, Map<Enum, List<CollisionHandler>>> collisionHandlerMap = new HashMap<>();
    static {
        //使用SPI机制加载所有的实体工厂
        ServiceLoader<EntityFactory> factories = ServiceLoader.load(EntityFactory.class);
        for (EntityFactory factory : factories) {
            registerEntityFactory(factory);
        }
    }

    public static void registerEntityFactory(EntityFactory factory) {
        System.out.println("注册实体工厂："+factory.getEntityName());
        entityFactories.put(factory.getEntityName(),factory);
    }
    public GameWorld(){
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyTyped(KeyEvent e) {
                System.out.println("Game World"+e.getKeyChar());
            }
        });
    }
    public void addCollisionHandler(Enum source,Enum target, CollisionHandler collisionHandler){

        Objects.requireNonNull(source, "源类型不能为null");
        Objects.requireNonNull(target, "目标类型不能为null");
        Objects.requireNonNull(collisionHandler, "碰撞处理器不能为null");

        if(!collisionHandlerMap.containsKey(source)){
            collisionHandlerMap.put(source, new HashMap<>());
        }
        Map<Enum, List<CollisionHandler>> enumListMap = collisionHandlerMap.get(source);
        if(!enumListMap.containsKey(target)){
            enumListMap.put(target, new ArrayList<>());
        }
        enumListMap.get(target).add(collisionHandler);
    }
    //将实体添加到游戏世界中
    //形参：entity-游戏实体
    public void addEntity(Entity entity){
        entity.setGameWorld(this);
        IdComponent component = entity.getComponent(IdComponent.class);
        if (component != null) {
            Entity entityById = getEntityById(component.getId());
            if(entityById != null){
                throw new IllegalStateException("实体ID重复:"+component.getId());
            }
        }
        ENTITIES.add(entity);
        entity.onAdded();
    }
    //将实体从游戏世界中移除
    //形参：entity-游戏实体
    public void removeEntity(Entity entity){
        ENTITIES.remove(entity);
        entity.onRemoved();
    }
    public List<Entity> getEntitiesByComponent(Class< ?  extends Component> componentClass){
        List<Entity> entities = new ArrayList<>();
        for (Entity entity : ENTITIES){
            List<Component> components =entity.getComponents();
            for (Component component : components){
                //检测component对象是否与componentClass兼容(类型一致，或者是子类)
                if (componentClass.isAssignableFrom(component.getClass())){
                    entities.add(entity);
                }
            }
        }
        return entities;
    }
    public Entity getEntityById(int id){
        List<Entity> entities = getEntitiesByComponent(IdComponent.class);
        for(Entity entity : entities){
            IdComponent idComponent = entity.getComponent(IdComponent.class);
            if(idComponent.getId() == id){
                return entity;
            }
        }
        return null;
    }

    public List<Entity> getEntitiesByName(String name){
        List<Entity> entities = getEntitiesByComponent(NameComponent.class);
        List<Entity> entitiesByName = new ArrayList<>();
        for(Entity entity : entities){
            NameComponent component = entity.getComponent(NameComponent.class);
            if(component.getName().equals(name)){
                entitiesByName.add(entity);
            }
        }
        return entitiesByName;
    }

    public Entity createEntity(String entityName) {
        return createEntity(entityName, null);
    }
    public Entity createEntity(String entityName, double x, double y){
        InitData data = new InitData();
        data.setPoint(new Point2D.Double(x, y));
        return createEntity(entityName, data);
    }

    public Entity createEntity(String entityName,InitData initData){
        EntityFactory entityFactory = entityFactories.get(entityName);
        Objects.requireNonNull(entityFactory, "实体工厂不存在" + entityName);
        if(initData == null){
            initData = new InitData();
        }

        try {
            Entity entity = entityFactory.create(initData);
            NameComponent component = entity.getComponent(NameComponent.class);
            if(component == null){
                entity.addComponent(new NameComponent(entityName));
            }
            addEntity(entity);
            return entity;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public void collision() {
        // 获得所有支持碰撞检测的实体对象
        List<Entity> entities = getEntitiesByComponent(CollisionComponent.class);
        Map<Enum, List<Entity>> entitiesType = new HashMap<>();
        // 分类
        for (Entity entity : entities) {
            CollisionComponent component = entity.getComponent(CollisionComponent.class);
            if (component != null) {
                Enum type = component.getType();
                if (!entitiesType.containsKey(type)) {
                    entitiesType.put(type, new ArrayList<>());
                }
                entitiesType.get(type).add(entity);
            }
        }
        // 检测
        Set<Map.Entry<Enum, Map<Enum, List<CollisionHandler>>>> entries = collisionHandlerMap.entrySet();
        for (Map.Entry<Enum, Map<Enum, List<CollisionHandler>>> entry : entries) {
            // 获得碰撞检测的源类型
            Enum source = entry.getKey();
            // 得到所有符合源类型条件的实体对象
            List<Entity> sourceEntities = entitiesType.get(source);
            if (sourceEntities == null || sourceEntities.isEmpty()) {
                continue;
            }
            // 获得所有碰撞检测的目标类型
            Map<Enum, List<CollisionHandler>> targetMap = entry.getValue();
            if (targetMap == null || targetMap.isEmpty()) {
                continue;
            }
            for (Map.Entry<Enum, List<CollisionHandler>> targetEntities : targetMap.entrySet()) {
                // 循环检测所有目标类型
                Enum target = targetEntities.getKey();
                List<CollisionHandler> collisionHandlers = targetEntities.getValue();
                // 得到所有符合目标类型条件的实体对象
                List<Entity> targetEntitiesList = entitiesType.get(target);
                // 循环检测所有源类型
                for (Entity sourceEntity : sourceEntities) {
                    CollisionComponent component = sourceEntity.getComponent(CollisionComponent.class);
                    Map<String, HotRectangle> sourceHotRectangles = component.getHotRectangles();
                    // 循环检测所有目标类型
                    for (Entity targetEntity : targetEntitiesList) {
                        // 碰撞检测
                        boolean flag = false;
                        CollisionComponent targetEntityComponent = targetEntity.getComponent(CollisionComponent.class);
                        Map<String, HotRectangle> targetHotRectangles = targetEntityComponent.getHotRectangles();
                        for (Map.Entry<String, HotRectangle> sourceHot : sourceHotRectangles.entrySet()) {
                            HotRectangle sourceHotRectangle = sourceHot.getValue();
                            for (Map.Entry<String, HotRectangle> targetHot : sourceHotRectangles.entrySet()) {
                                HotRectangle targetHotRectangle = targetHot.getValue();
                                // 使用实体的坐标叠加加热区的偏移量得到热区的实际位置
                                Rectangle sourceHotRect = new Rectangle(sourceHotRectangle);
                                sourceHotRect.setLocation((int) (sourceEntity.getX() + sourceHotRect.getX()), (int) (sourceEntity.getY() + sourceHotRect.getY()));
                                Rectangle targetHotRect = new Rectangle(targetHotRectangle);
                                targetHotRect.setLocation((int) (targetEntity.getX() + targetHotRect.getX()), (int) (targetEntity.getY() + targetHotRect.getY()));


                                // 检测两个矩形区域是否相交
                                if (sourceHotRect.contains(targetHotRect)) {
                                    flag = true;
                                    // 精准检测
                                    for (CollisionHandler collisionHandler : collisionHandlers) {
                                        collisionHandler.onCollision(sourceEntity, targetEntity, sourceHotRectangle, targetHotRectangle);

                                    }
                                }
                            }
                        }
                        if (flag) {
                            // 模糊检测
                            for (CollisionHandler collisionHandler : collisionHandlers) {
                                collisionHandler.onCollision(sourceEntity, targetEntity);
                            }
                        }
                    }
                }
            }

        }
    }


    //绘制界面，由jdk自动调用
    @Override
    protected void paintComponent(Graphics g){
//        清屏
        g.clearRect(0,0,getWidth(),getHeight());
        //调用所有实体中的组件的update方法
        //获取当前帧消耗的毫秒数，并且将其转换为秒
        double tpf =localTimeUtil.getTimeConsumed()/1000;
        for (Entity entity : ENTITIES){
            entity.update(tpf);
        }
        List<Entity> entities = getEntitiesByComponent(ViewComponent.class);
        Collections.sort(entities, new Comparator<Entity>() {
            @Override
            public int compare(Entity o1, Entity o2) {
                return o1.getViewComponent().getZIndex() - o2.getViewComponent().getZIndex();
            }
        });
        for (Entity entity : entities) {
            ViewComponent component = entity.getViewComponent();
            component.draw(g);
        }
        collision();
        localTimeUtil.reset();
        //告知系统重新绘制界面（会再次调用paintComponent方法）
        this.repaint();
    }


}